﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Windows.Controls;
using System.Windows.Input;
using HeritageAcademy.Models.DataModels;
using HeritageAcademy.Models.Loaders;
using HeritageAcademy.Shared;

namespace HeritageAcademy.Models.ViewModels
{
    /// <summary>
    /// The main ViewModel class for the wizard.
    /// This class contains the various pages shown
    /// in the workflow and provides navigation
    /// between the pages.
    /// </summary>
    public class TestWizardViewModel : INotifyPropertyChanged
    {
        #region Parameters

        TestResultsLoader _resultsLoader;
        TestWizardPageViewModel _currentPage;
        TestDataLoader _currentTest;
        ReadOnlyCollection<TestDataLoader> _testLoaders;
        ReadOnlyCollection<TestWizardPageViewModel> _pages;
        string _selectedTestTitle;
        UsersConfigLoader _userConfigLoader;
        
        RelayCommand _cancelCommand;
        RelayCommand _moveNextCommand;
        RelayCommand _movePreviousCommand;

        readonly CourseViewModel _course;

        #endregion

        #region Ctor

        public TestWizardViewModel(CourseViewModel course)
        {
            _course = course;
            _userConfigLoader = new UsersConfigLoader(new Users());
            _resultsLoader = new TestResultsLoader(new TestResults(), _userConfigLoader.ActiveUser.Id);
        }
        #endregion

        #region Commands

        #region CancelCommand

        /// <summary>
        /// Returns the command which, when executed, cancels the order 
        /// and causes the Wizard to be removed from the user interface.
        /// </summary>
        public ICommand CancelCommand
        {
            get { return _cancelCommand ?? (_cancelCommand = new RelayCommand(param => CancelOrder())); }
        }

        void CancelOrder()
        {
            OnRequestClose();
        }

        #endregion

        #region MovePreviousCommand

        /// <summary>
        /// Returns the command which, when executed, causes the CurrentPage 
        /// property to reference the previous page in the workflow.
        /// </summary>
        public ICommand MovePreviousCommand
        {
            get
            {
                return _movePreviousCommand ?? (_movePreviousCommand = new RelayCommand(param => MoveToPreviousPage(),
                                                                                                                                            param => CanMoveToPreviousPage));
            }
        }

        public bool CanMoveToPreviousPage
        {
            get { return 0 < CurrentPageIndex; }
        }

        public void MoveToPreviousPage()
        {
            if (CanMoveToPreviousPage)
                CurrentPage = Pages[CurrentPageIndex - 1];
        }

        #endregion

        #region MoveNextCommand

        /// <summary>
        /// Returns the command which, when executed, causes the CurrentPage 
        /// property to reference the next page in the workflow.  If the user
        /// is viewing the last page in the workflow, this causes the Wizard
        /// to finish and be removed from the user interface.
        /// </summary>
        public ICommand MoveNextCommand
        {
            get
            {
                return _moveNextCommand ?? (_moveNextCommand = new RelayCommand(param => MoveToNextPage(),
                                                                                                                                param => CanMoveToNextPage));
            }
        }

        public bool CanMoveToNextPage
        {
            get { return CurrentPage != null && CurrentPage.IsValid(); }
        }

        public void MoveToNextPage()
        {
            if (!CanMoveToNextPage) return;
            if (CurrentPageIndex < Pages.Count - 1)
                CurrentPage = Pages[CurrentPageIndex + 1];
            else
                OnRequestClose();
        }

        #endregion

        #endregion

        #region Properties

        public ReadOnlyCollection<TestDataLoader> TestLoaders
        {
            get
            {
                if (_testLoaders == null) GetAllTests();
                return _testLoaders;
            }
        }

        /// <summary>
        /// Get overall number of tests
        /// </summary>
        public int TestCount
        {
            get 
            {
                if (_testLoaders == null) GetAllTests();
                return (_testLoaders != null) ? _testLoaders.Count : -1; 
            }
        }

        public IEnumerable<string> TestsTitles
        {
            get 
            {
                var titles = TestLoaders.Select(testLoader => testLoader.Model.Title).ToList();
                SelectedTestTitle = titles[0];
                return titles;
            }
        }

        public string SelectedTestTitle
        {
            get { return _selectedTestTitle; }
            set 
            { 
                _selectedTestTitle = value;
                SetCurrentTest(value);
                OnPropertyChanged("SelectedTestTitle");
            }
        }

        public TestDataLoader CurrentTest
        {
            get { return _currentTest; }
            set
            {
                _currentTest = value;
                OnPropertyChanged("CurrentTest");
            }
        }


        /// <summary>
        /// Returns the page ViewModel that the user is currently viewing.
        /// </summary>
        public TestWizardPageViewModel CurrentPage
        {
            get { return _currentPage; }
            private set
            {
                if (value == _currentPage)
                    return;

                if (_currentPage != null)
                    _currentPage.IsCurrentPage = false;

                _currentPage = value;

                if (_currentPage != null)
                    _currentPage.IsCurrentPage = true;

                OnPropertyChanged("CurrentPage");
                OnPropertyChanged("IsOnLastPage");
            }
        }

        /// <summary>
        /// Returns true if the user is currently viewing the last page 
        /// in the workflow.  This property is used by TestWizardView
        /// to switch the Next button's text to "Finish" when the user
        /// has reached the final page.
        /// </summary>
        public bool IsOnLastPage
        {
            get { return CurrentPageIndex == Pages.Count - 1; }
        }

        /// <summary>
        /// Returns true if the user is currently viewing the last page 
        /// in the workflow.  This property is used by TestWizardView
        /// to switch the Next button's text to "Finish" when the user
        /// has reached the final page.
        /// </summary>
        public bool IsOnFirstPage
        {
            get { return CurrentPageIndex == 0; }
        }

        /// <summary>
        /// Returns a read-only collection of all page ViewModels.
        /// </summary>
        public ReadOnlyCollection<TestWizardPageViewModel> Pages
        {
            get
            {
                if (_pages == null) CreatePages();
                return _pages;
            }
        }

        /// <summary>
        /// Get overall number of pages in test
        /// </summary>
        public int PageCount
        {
            get { return Pages.Count; }
        }

        #endregion

        #region Private Helpers

        void GetAllTests()
        {
            if (IOHelper.TestDataFilesPaths(_course.Model.Title) != null)
            {
                var data = IOHelper.TestDataFilesPaths(_course.Model.Title).Select(file => new TestDataLoader(new TestWizard(), file)).ToList();
                _testLoaders = new ReadOnlyCollection<TestDataLoader>(data);
            }
        }

        void SetCurrentTest(string title)
        {
            foreach (var testLoader in TestLoaders.Where(testLoader => testLoader.Model.Title == title))
            {
                CurrentTest = testLoader;
            }
        }

        void CreatePages()
        {
            var number = 0;
            var pages = CurrentTest.Model.TestPage.Select(page => new TestWizardPageViewModel(_resultsLoader) {
                                                                          Source = Path.Combine(CurrentTest.Folder, page.Source), Number = ++number, CurrentTestId = CurrentTest.Model.Id
                                                                      }).ToList();

            _pages = new ReadOnlyCollection<TestWizardPageViewModel>(pages);
            
            CurrentPage = Pages[0]; // TODO: it should be here ???
            _userConfigLoader = new UsersConfigLoader(new Users());
            _resultsLoader = new TestResultsLoader(new TestResults(), _userConfigLoader.ActiveUser.Id);
        }
      
        int CurrentPageIndex
        {
            get
            {
                if (CurrentPage == null)
                {
                    Debug.Fail("Why is the current page null?");
                }

                return Pages.IndexOf(CurrentPage);
            }
        }

        void OnRequestClose()
        {
            var handler = RequestClose;
            if (handler != null)
                handler(this, EventArgs.Empty);
        }

        #endregion

        #region Public Methods

        public void InitializeCurentPageAnswers(Control ctrl)
        {
            _currentPage.GetAvailableAnswers(ctrl);
        }

        #endregion

        #region Events

        /// <summary>
        /// Raised when the wizard should be removed from the UI.
        /// </summary>
        public event EventHandler RequestClose;

        #endregion

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        void OnPropertyChanged(string propertyName)
        {
            var handler = PropertyChanged;
            if (handler != null)
                handler(this, new PropertyChangedEventArgs(propertyName));
        }

        #endregion
    }
}
